﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using OA.Model;
using System.ComponentModel.DataAnnotations.Schema;
using OA.Core;
using OA.Model.DB;
using Microsoft.EntityFrameworkCore;
using OA.Core.Other;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace OA.BLL
{
    public class DbContextFactory
    {
        public MyDbContext DbContext { get; set; }
        private static MyDbContext CreateDbContext()
        {
            MyDbContext myDbContext = (MyDbContext)CallContext.GetData("DbContext");
            if (myDbContext == null)
            {
                myDbContext = new MyDbContext();
                CallContext.SetData("DbContext", myDbContext);
            }
            return myDbContext;
        }

        public DbContextFactory()
        {
            DbContext = CreateDbContext();
        }

        public int AddInfo<T>(T info) where T : BaseInfo
        {            
            info.CreateDateTime = DateTime.Now;
            DbContext.Set<T>().Add(info);
            return DbContext.SaveChanges();
        }

        public int AddRange<T>(List<T> list) where T : BaseInfo
        {
            foreach (T t in list)
            {
                t.CreateDateTime = DateTime.Now;
            }            
            DbContext.Set<T>().AddRange(list);
            return DbContext.SaveChanges();
        }


        public T GetInfo<T>(long id) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(m => m.ID == id).FirstOrDefault();
        }

        public T GetInfoAsNoTracking<T>(long id) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(m => m.ID == id).AsNoTracking().FirstOrDefault();
        }

        public K GetInfo<T, K>(long id, Expression<Func<T, K>> Map) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(m => m.ID == id).Select(Map).FirstOrDefault();
        }    

        public K GetInfo<T, K>(Expression<Func<T,bool>> where, Expression<Func<T, K>> Map) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(where).Select(Map).FirstOrDefault();
        }

        public T GetInfo<T>(Expression<Func<T, bool>> where) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(where).FirstOrDefault();
        }

        public T GetInfoAsNoTracking<T>(Expression<Func<T, bool>> where) where T : BaseInfo
        {
            return DbContext.Set<T>().Where(where).AsNoTracking().FirstOrDefault();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="order"></param>
        ///<param name = "sortType" > 1:顺序 2：倒序</param>
        /// <returns></returns>
        public T GetLastInfo<T>(Expression<Func<T, bool>> where, Expression<Func<T, long>> order, int sortType) where T : BaseInfo
        {
            if (order == null) return null;
            if (sortType == 1)
            {
                return DbContext.Set<T>().Where(where).OrderBy(order).FirstOrDefault();
            }
            else
            {
                return DbContext.Set<T>().Where(where).OrderByDescending(order).FirstOrDefault();
            }
        }

        public T GetLastInfoAsNoTracking<T>(Expression<Func<T, bool>> where, Expression<Func<T, long>> order, int sortType) where T : BaseInfo
        {
            if (order == null) return null;
            if (sortType == 1)
            {
                return DbContext.Set<T>().Where(where).OrderBy(order).AsNoTracking().FirstOrDefault();
            }
            else
            {
                return DbContext.Set<T>().Where(where).OrderByDescending(order).AsNoTracking().FirstOrDefault();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="order"></param>
        ///<param name = "sortType" > 1:顺序 2：倒序</param>
        /// <returns></returns>
        public T GetLastInfo<T>(Expression<Func<T, bool>> where, Expression<Func<T, string>> order, int sortType) where T : BaseInfo
        {
            if (order == null) return null;
            if (sortType == 1)
            {
                return DbContext.Set<T>().Where(where).OrderBy(order).FirstOrDefault();
            }
            else
            {
                return DbContext.Set<T>().Where(where).OrderByDescending(order).FirstOrDefault();
            }
        }


        public int Delete<T>(long id) where T : BaseInfo
        {
            T oldInfo = this.DbContext.Set<T>().Where(m=>m.ID==id).FirstOrDefault();
            if (oldInfo==null) return 0;
            DbContext.Set<T>().Remove(oldInfo);
            return DbContext.SaveChanges();
        }

        public int Delete<T>(Expression<Func<T, bool>> where) where T : BaseInfo
        {
            var olds = this.DbContext.Set<T>().Where(where).ToList();
            if (olds == null||olds.Count==0) return 0;             
            DbContext.Set<T>().RemoveRange(olds);
            return DbContext.SaveChanges();
        }


        /// <summary>
        /// 批量更新数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where">查询条件</param>
        /// <param name="t">更新值</param>
        /// <param name="fileds">更新字段</param>
        /// <param name="userID"></param>
        /// <returns></returns>
        public int UpdateByWhere<T>(Expression<Func<T, bool>> where, T t, string[] fileds) where T : BaseInfo
        {
            var list = DbContext.Set<T>().Where(where).ToList();
            foreach (T item in list)
            {
                EntityEntry<T> entry = DbContext.Entry<T>(item);
                entry.State = EntityState.Unchanged;
                MyStringComparer stringComparer = new MyStringComparer();
                foreach (System.Reflection.PropertyInfo p in item.GetType().GetProperties())
                {
                    if (p.GetMethod.IsVirtual) continue;
                    var notMappedAttribute = p.GetCustomAttributes(typeof(NotMappedAttribute)).FirstOrDefault();
                    if (notMappedAttribute != null) { continue; }
                    if (fileds != null && !fileds.Contains(p.Name, stringComparer)) continue;
                    if (p.Name == "ID" || p.Name == "Guid" || p.Name == "DbOperateUserID" || p.Name == "CreateDateTime" || p.Name == "UpdateFileds") continue;
                    entry.Property(p.Name).IsModified = true;
                    p.SetValue(item, p.GetValue(t));
                }
            }
            return DbContext.SaveChanges();
        }


        public int Update<T>(List<T> list, string[] fileds) where T : BaseInfo
        {
            foreach (T item in list)
            {
                JsonSerializerSettings settings = new JsonSerializerSettings();
                settings.MissingMemberHandling = MissingMemberHandling.Ignore;
                T temp = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(item, settings));
                EntityEntry<T> entry = DbContext.Entry<T>(item);
                entry.State = EntityState.Unchanged;
                MyStringComparer stringComparer = new MyStringComparer();
                foreach (System.Reflection.PropertyInfo p in item.GetType().GetProperties())
                {
                    if (p.GetMethod.IsVirtual) continue;
                    var notMappedAttribute = p.GetCustomAttributes(typeof(NotMappedAttribute)).FirstOrDefault();
                    if (notMappedAttribute != null) { continue; }
                    if (fileds != null && !fileds.Contains(p.Name, stringComparer)) continue;
                    if (p.Name == "ID" || p.Name == "Guid" || p.Name == "DbOperateUserID" || p.Name == "CreateDateTime" || p.Name == "UpdateFileds") continue;
                    entry.Property(p.Name).IsModified = true;
                    p.SetValue(item, p.GetValue(temp));
                }
            }
            return DbContext.SaveChanges();
        }



        public int Update<T>(T oldInfo, T t, string[] fileds) where T : BaseInfo
        {
            EntityEntry<T> entry = DbContext.Entry<T>(oldInfo);
            entry.State = EntityState.Unchanged;
            MyStringComparer stringComparer = new MyStringComparer();
            foreach (System.Reflection.PropertyInfo p in t.GetType().GetProperties())
            {
                if (p.GetMethod.IsVirtual) continue;
                var notMappedAttribute = p.GetCustomAttributes(typeof(NotMappedAttribute)).FirstOrDefault();
                if (notMappedAttribute != null) { continue; }
                if (fileds != null && !fileds.Contains(p.Name, stringComparer) && p.Name != "UpdatedDateTime") continue;
                if (p.Name == "ID" || p.Name == "Guid" || p.Name == "DbOperateUserID" || p.Name == "CreateDateTime" || p.Name == "UpdateFileds") continue;
                entry.Property(p.Name).IsModified = true;
                p.SetValue(oldInfo, p.GetValue(t));
            }
            return DbContext.SaveChanges();
        }


        public int Update<T>(T oldInfo, T t, string[] fileds, string[] filed2) where T : BaseInfo
        {

            EntityEntry<T> entry = DbContext.Entry<T>(oldInfo);
            entry.State = EntityState.Unchanged;
            MyStringComparer stringComparer = new MyStringComparer();
            foreach (System.Reflection.PropertyInfo p in t.GetType().GetProperties())
            {
                if (p.GetMethod.IsVirtual) continue;
                var notMappedAttribute = p.GetCustomAttributes(typeof(NotMappedAttribute)).FirstOrDefault();
                if (notMappedAttribute != null) { continue; }
                if (fileds != null && fileds.Length > 0 && !fileds.Contains(p.Name, stringComparer) && p.Name != "UpdatedDateTime") continue;
                if (filed2 != null && filed2.Contains(p.Name)) continue;
                if (p.Name == "ID" || p.Name == "Guid" || p.Name == "DbOperateUserID" || p.Name == "CreateDateTime" || p.Name == "UpdateFileds") continue;
                entry.Property(p.Name).IsModified = true;
                p.SetValue(oldInfo, p.GetValue(t));
            }
            return DbContext.SaveChanges();
        }

        public int Update<T>(long id, T t, string[] fileds) where T : BaseInfo
        {
            T oldInfo = this.GetInfo<T>(id);
            if (oldInfo == null)
            {
                return 0;
            }

            EntityEntry<T> entry = DbContext.Entry<T>(oldInfo);
            entry.State = EntityState.Unchanged;
            MyStringComparer stringComparer = new MyStringComparer();
            foreach (System.Reflection.PropertyInfo p in t.GetType().GetProperties())
            {
                if (p.GetMethod.IsVirtual) continue;
                if (fileds != null && !fileds.Contains(p.Name, stringComparer)) continue;
                if (p.Name == "ID" || p.Name == "Guid" || p.Name == "DbOperateUserID" || p.Name == "CreateDateTime" || p.Name == "UpdateFileds") continue;
                entry.Property(p.Name).IsModified = true;
                p.SetValue(oldInfo, p.GetValue(t));
            }
            return DbContext.SaveChanges();
        }


        public int ExecuteSql(string sql, params SqlParameter[] pars)
        {
            return DbContext.Database.ExecuteSqlRaw(sql, pars);
        }

        public int ExecuteSql(string sql)
        {

            return DbContext.Database.ExecuteSqlRaw(sql);
        }

        /// <summary>
        /// 执行sql语句 视图 存储过程("dbo.GetDestinationSummary @p0, @p1", country, keyWords);
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="pars"></param>
        /// <returns></returns>
        public List<T> ExecuteQuery<T>(string sql, params SqlParameter[] pars) where T : class, new()
        {
            return DbContext.Database.SqlQuery<T>(sql, pars).ToList();
        }



        /// <summary>
        /// 获取列表，多获取一条来判断是否有下一页
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="order">排序Func 倒序</param>
        /// <returns></returns>
        public List<K> GetList<T, K>(Expression<Func<T, bool>> where, int pageIndex, int pageSize, Expression<Func<T, K>> selector, Expression<Func<T, long>> order = null) where T : BaseInfo where K : SimpleInfo
        {
            if (order == null) { order = m => m.ID; }
            return DbContext.Set<T>().Where(where).OrderByDescending(order).Select(selector).Skip((pageIndex - 1) * pageSize).Take(pageSize + 1).AsNoTracking().ToList();
        }

        /// <summary>
        /// 获取所有记录
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="where"></param>
        /// <param name="selector"></param>
        /// <param name="order"></param>
        /// <param name="order">2:倒序</param>
        /// <returns></returns>
        public List<K> GetAll<T, K>(Expression<Func<T, bool>> where, Expression<Func<T, K>> selector, Expression<Func<T, long>> order = null, int sortType = 2) where T : BaseInfo where K : SimpleInfo
        {
            if (order == null) { order = m => m.ID; }
            if (sortType == 2)
            {
                return DbContext.Set<T>().Where(where).OrderByDescending(order).Select(selector).AsNoTracking().ToList();
            }
            else
            {
                return DbContext.Set<T>().Where(where).OrderBy(order).Select(selector).AsNoTracking().ToList();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="order"></param>
        /// <param name="sortType">2:倒序</param>
        /// <returns></returns>
        public List<T> GetAll<T>(Expression<Func<T, bool>> where, Expression<Func<T, long>> order = null, int sortType = 2) where T : BaseInfo
        {
            //将Func用Expression包裹，Expression包裹后，会直接将WHERE条件放到数据库中查询，而Func会在内存中查询
            if (order == null) { order = m => m.ID; }
            if (sortType == 2)
            {
                return DbContext.Set<T>().Where(where).OrderByDescending(order).AsNoTracking().ToList();
            }
            else
            {
                return DbContext.Set<T>().Where(where).OrderBy(order).AsNoTracking().ToList();
            }
        }

        /// <summary>
        /// 获取列表 排序字段类型为 long
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="rowCount">总条数</param>
        /// <param name="order"></param>
        /// <param name="sortType">1:顺序 2：倒序</param>
        /// <returns></returns>
        public PageList<K> GetPageList<T, K>(Expression<Func<T, bool>> where, int pageIndex, int pageSize, Expression<Func<T, K>> selector, Expression<Func<T, int>> order = null, int sortType = 2) where T : BaseInfo where K : SimpleInfo
        {
            //将Func用Expression包裹，Expression包裹后，会直接将WHERE条件放到数据库中查询，而Func会在内存中查询
            //AsNoTracking
            long rowCount = DbContext.Set<T>().Where(where).Select(m => m.ID).LongCount();
            if (order == null) { order = m => m.ID; }
            PageList<K> result = new PageList<K>();
            result.CurrentPage = pageIndex;
            double temp = rowCount / pageSize;
            result.RowCount = rowCount;
            result.PageSize = pageSize;
            if (sortType == 1)
            {
                result.List = DbContext.Set<T>().Where(where).OrderBy(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).Select(selector).AsNoTracking().ToList();
            }
            else
            {
                result.List = DbContext.Set<T>().Where(where).OrderByDescending(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).Select(selector).AsNoTracking().ToList();
            }
            return result;
        }


        public PageList<T> GetPageList<T>(Expression<Func<T, bool>> where, int pageIndex, int pageSize, Expression<Func<T, int>> order = null, int sortType = 2) where T : BaseInfo
        {
            //将Func用Expression包裹，Expression包裹后，会直接将WHERE条件放到数据库中查询，而Func会在内存中查询
            //AsNoTracking
            long rowCount = DbContext.Set<T>().Where(where).Select(m => m.ID).LongCount();
            if (order == null) { order = m => m.ID; }
            PageList<T> result = new PageList<T>();
            result.CurrentPage = pageIndex;
            double temp = rowCount / pageSize;
            result.RowCount = rowCount;
            result.PageSize = pageSize;
            if (sortType == 1)
            {
                result.List = DbContext.Set<T>().Where(where).OrderBy(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
            }
            else
            {
                result.List = DbContext.Set<T>().Where(where).OrderByDescending(order).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
            }

            return result;
        }


        /// <summary>
        /// 获取列表 排序字段类型为string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="where"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="rowCount">总条数</param>
        /// <param name="order"></param>
        /// <param name="sortType">1:顺序 2：倒序</param>
        /// <returns></returns>
        public PageList<K> GetPageList2<T, K>(Expression<Func<T, bool>> where, int pageIndex, int pageSize, Expression<Func<T, K>> selector, Expression<Func<T, string>> order, int sortType = 2) where T : BaseInfo where K : SimpleInfo
        {
            long rowCount = DbContext.Set<T>().Where(where).Select(m => m.ID).LongCount();
            if (order == null) { order = m => m.ID.ToString("D10"); }
            PageList<K> result = new PageList<K>();
            result.CurrentPage = pageIndex;
            double temp = rowCount / pageSize;
            result.RowCount = rowCount;
            if (sortType == 1)
            {
                result.List = DbContext.Set<T>().Where(where).OrderBy(order).Select(selector).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
            }
            else
            {
                result.List = DbContext.Set<T>().Where(where).OrderByDescending(order).Select(selector).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
            }
            return result;
        }




    }
}
